feat(list): drag and drop reordering (#DS-4454) - #1911
Conversation
Options of `kbq-list-selection` can be reordered by dragging or with `Alt` + arrow keys once `draggable` is set, and moved between lists connected via `connectedTo`. The list never mutates the projected data: it reports the move through the `dropped` event and the consumer applies it. The new position is announced to assistive tech only once that move has actually been applied, and `aria-keyshortcuts` advertises the keyboard alternative to dragging. Dragging inside `kbq-optgroup` or `cdk-virtual-scroll-viewport` reports indices that do not address the backing array, so both now warn in development mode.
There was a problem hiding this comment.
Pull request overview
Adds opt-in drag-and-drop (and keyboard) reordering/transfer for kbq-list-selection, including accessible announcements and documentation/examples, while keeping the list “data-immutable” (consumer applies reorders based on a dropped event).
Changes:
- Introduces
draggable,connectedTo, anddroppedonKbqListSelection, plus keyboard reordering/transfer viaAlt+ arrow keys and live-region announcements. - Adds styling for drag preview/placeholder/animations and updates a11y locale strings to support move announcements.
- Adds docs examples, dev-app demos, and unit/e2e coverage for drag/drop + keyboard flows.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/public_api_guard/components/list.api.md | Public API snapshot updated for new list drag/drop inputs/outputs and types. |
| tools/public_api_guard/components/core.api.md | Public API snapshot updated for new a11y locale key (listOptionMoved). |
| packages/e2e/routes.ts | Registers new list drag-and-drop E2E route/component. |
| packages/docs-examples/example-module.ts | Adds new live examples entries for draggable list variants. |
| packages/docs-examples/components/list/list-draggable/list-draggable-example.ts | New docs example: in-list reordering via dropped. |
| packages/docs-examples/components/list/list-draggable-connected/list-draggable-connected-example.ts | New docs example: transferring options between connected lists. |
| packages/docs-examples/components/list/index.ts | Exports and registers the new list draggable examples module-side. |
| packages/components/list/list.scss | Adds drag/drop interaction styling (placeholder, preview, cursor, transitions). |
| packages/components/list/list.ru.md | Documents draggable lists + keyboard shortcuts (RU). |
| packages/components/list/list.en.md | Documents draggable lists + keyboard shortcuts (EN). |
| packages/components/list/list-tokens.scss | Adds tokens for drag preview surface/shadow; ensures preview inherits tokens in <body>. |
| packages/components/list/list-selection.component.ts | Core implementation: CDK drag/drop wiring, keyboard move/transfer, dropped event, live-region announcements, dev warnings. |
| packages/components/list/list-selection.component.spec.ts | Unit + axe coverage for drag/drop opt-in, keyboard behavior, announcements, and shortcuts. |
| packages/components/list/e2e.ts | Adds E2E fixture component with two connected draggable lists and consumer-applied reorder/transfer. |
| packages/components/list/e2e.playwright-spec.ts | Adds Playwright coverage for pointer drag, keyboard reorder/transfer, and transition settling behavior. |
| packages/components/core/locales/types.ts | Extends KbqA11yLocaleConfiguration with listOptionMoved announcement template. |
| packages/components/core/locales/tk-TM.ts | Adds listOptionMoved locale string (tk-TM). |
| packages/components/core/locales/ru-RU.ts | Adds listOptionMoved locale string (ru-RU). |
| packages/components/core/locales/pt-BR.ts | Adds listOptionMoved locale string (pt-BR). |
| packages/components/core/locales/es-LA.ts | Adds listOptionMoved locale string (es-LA). |
| packages/components/core/locales/en-US.ts | Adds listOptionMoved locale string (en-US). |
| packages/components-dev/list/template.html | Adds dev-app demos for draggable reorder and connected transfer. |
| packages/components-dev/list/module.ts | Wires demo data + handlers for the new draggable behaviors in dev app. |
Suppressed comments (1)
packages/components/list/list-selection.component.ts:814
- To ensure unsupported-container warnings are emitted when
draggablebecomes enabled after initial render (e.g. dynamic bindings), callwarnOnUnsupportedDragContainer()fromsyncDraggableState(), which already runs whendraggable/disabledchange.
/** Keeps the underlying CDK directives in sync with the resolved `draggable` state. */
private syncDraggableState(): void {
this.dropList.disabled = !this.draggable;
this.options?.forEach((option) => option.syncDraggableState());
this.changeDetectorRef.markForCheck();
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Visit the preview URL for this PR (updated for commit 22bc05d): https://koobiq-next--prs-1911-o55jwo31.web.app (expires Fri, 21 Aug 2026 11:40:16 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c |
`KbqListOptionDragData` is only read inside the component file, so it no longer leaves the package and no longer widens the public API surface. `warnOnUnsupportedDragContainer()` now also runs from `syncDraggableState()`, so enabling `draggable` after the initial render still warns. It tolerates being called before the content children exist and reports each list only once.
Dragging an option no longer opens a gap. The surrounding options stay where they are, the dragged one keeps its slot as a faded row, and a line with a dot marks the position the option would land in — in the connected list too, and along the main axis of a horizontal list. CDK's sorting is switched off to keep the list still, which also makes the index it reports on drop unusable, so the target is derived from the pointer instead. The dragged option is left out of that measurement, which makes the resulting gap index the very index `moveItemInArray` and `transferArrayItem` expect. Three CDK behaviours had to be handled: `enter()` ignores `sortingDisabled` and pushes the placeholder into whichever list is hovered, so a foreign placeholder is hidden; `hasAnchor` keeps the faded row in the origin list during a transfer; and the drop animation is dropped, because it would fly the preview back to the slot the drag started from and read as a rejected drop.
The indicator is a plain line now. Removes the cap pseudo-element in both orientations and the size token that only fed it.
| event.preventDefault(); | ||
| } | ||
|
|
||
| if (this.draggable && isAltOnly(event) && [UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW].includes(keyCode)) { |
There was a problem hiding this comment.
Не работает на MacOS - происходит скролл страницы
Options of
kbq-list-selectioncan be reordered by dragging or withAlt+ arrow keys oncedraggableis set, and moved between lists connected viaconnectedTo.The list never mutates the projected data: it reports the move through the
droppedevent and the consumer applies it. The new position is announced to assistive tech only once that move has actually been applied, andaria-keyshortcutsadvertises the keyboard alternative to dragging.Dragging inside
kbq-optgrouporcdk-virtual-scroll-viewportreports indices that do not address the backing array, so both now warn in development mode.